﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace L4View.Infrastructure
{
    public abstract class MessageMonitor : DisposableObject, IMessageMonitor, IMessageHandler
    {
        private readonly IParser _parser;

        private readonly ISource _source;

        private readonly Thread _thread;

        private readonly HashSet<IMessageHandler> _messageSinks;

        private volatile bool _stopThread;

        private volatile bool _isRunning;

        protected MessageMonitor(ISource source, IParser parser, int queueCapacity)
        {
            if (parser == null)
                throw new ArgumentNullException("parser");

            if (source == null)
                throw new ArgumentNullException("source");

            var messageQueue = new MessageQueue(queueCapacity);
            
            messageQueue.RegisterSink(this);

            _parser = parser;
            _parser.AssignMessageSink(messageQueue);

            _source = source;
            
            _thread = new Thread(this.Run);

            _messageSinks = new HashSet<IMessageHandler>();
        }

        public abstract Guid UniqueId
        {
            get;
        }

        public bool IsRunning
        {
            get { return _isRunning || _thread.IsAlive ; }
        }

        public void RegisterSink(IMessageHandler sink)
        {
            _messageSinks.Add(sink);
        }

        public void UnregisterSink(IMessageHandler sink)
        {
            _messageSinks.Remove(sink);
        }

        public void Start(bool wait)
        {
            if (IsRunning)
                Stop();
            
            _thread.Start();

            while (wait && !IsRunning)
                _thread.Join(1);
        }

        public void Stop()
        {
            if (!IsRunning)
                return;
            
            _stopThread = true;
            _thread.Join(100);
            
            if (IsRunning)
                _thread.Abort();
            
            _isRunning = false;
            _stopThread = false;
        }

        protected void Run()
        {
            _isRunning = true;

            while (!_stopThread)
            {
                _parser.Parse(_source.Read());
            }

            _isRunning = false;
        }

        void IMessageHandler.Transfer(bool stop)
        {
            foreach (var sink in _messageSinks)
                sink.Transfer(stop);
        }

        void IMessageHandler.PutMessage(IMessage message)
        {
            foreach (var sink in _messageSinks)
                sink.PutMessage(message);
        }

        public override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (disposing)
            {
                if (IsRunning)
                    Stop();
            }
        }
    }
}
